/*
 * citygml4j - The Open Source Java API for CityGML
 * https://github.com/citygml4j
 *
 * Copyright 2013-2020 Claus Nagel <claus.nagel@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.citygml4j.model.gml.geometry.primitives;

import org.citygml4j.builder.copy.CopyBuilder;
import org.citygml4j.geometry.BoundingBox;
import org.citygml4j.model.common.base.ModelObjects;
import org.citygml4j.model.common.visitor.GMLFunctor;
import org.citygml4j.model.common.visitor.GMLVisitor;
import org.citygml4j.model.common.visitor.GeometryFunctor;
import org.citygml4j.model.common.visitor.GeometryVisitor;
import org.citygml4j.model.gml.GMLClass;
import org.citygml4j.model.gml.base.CoordinateListProvider;
import org.citygml4j.model.gml.basicTypes.Coordinates;

import java.util.ArrayList;
import java.util.List;

public class Point extends AbstractGeometricPrimitive implements CoordinateListProvider {
	private DirectPosition pos;
	private Coordinates coordinates;
	private Coord coord;

	public BoundingBox calcBoundingBox() {
		BoundingBox bbox = new BoundingBox();
		List<Double> points = toList3d();

		for (int i = 0; i < points.size(); i += 3)
			bbox.update(points.get(i), points.get(i + 1), points.get(i + 2));
		
		return bbox;
	}

	public GMLClass getGMLClass() {
		return GMLClass.POINT;
	}
	
	public Coord getCoord() {
		return coord;
	}

	public Coordinates getCoordinates() {
		return coordinates;
	}

	public DirectPosition getPos() {
		return pos;
	}

	public boolean isSetCoord() {
		return coord != null;
	}

	public boolean isSetCoordinates() {
		return coordinates != null;
	}

	public boolean isSetPos() {
		return pos != null;
	}

	public void setCoord(Coord coord) {
		this.coord = ModelObjects.setParent(coord, this);
	}

	public void setCoordinates(Coordinates coordinates) {
		this.coordinates = ModelObjects.setParent(coordinates, this);
	}

	public void setPos(DirectPosition pos) {
		this.pos = ModelObjects.setParent(pos, this);
	}

	@Override
	public List<Double> toList3d() {
		List<Double> tmp = new ArrayList<>();

		if (isSetPos())
			tmp.addAll(pos.toList3d());
		else if (isSetCoord())
			tmp.addAll(coord.toList3d());
		else if (isSetCoordinates())
			tmp.addAll(coordinates.toList3d());

		return tmp;
	}

	public void unsetCoord() {
		coord = ModelObjects.setNull(coord);
	}

	public void unsetCoordinates() {
		coordinates = ModelObjects.setNull(coordinates);
	}

	public void unsetPos() {
		pos = ModelObjects.setNull(pos);
	}

	@Override
	public Object copyTo(Object target, CopyBuilder copyBuilder) {
		Point copy = (target == null) ? new Point() : (Point)target;		
		super.copyTo(copy, copyBuilder);
		
		if (isSetPos()) {
			copy.setPos((DirectPosition)copyBuilder.copy(pos));
			if (copy.getPos() == pos)
				pos.setParent(this);
		}
		
		if (isSetCoordinates()) {
			copy.setCoordinates((Coordinates)copyBuilder.copy(coordinates));
			if (copy.getCoordinates() == coordinates)
				coordinates.setParent(this);
		}
		
		if (isSetCoord()) {
			copy.setCoord((Coord)copyBuilder.copy(coord));
			if (copy.getCoord() == coord)
				coord.setParent(this);
		}
		
		return copy;
	}

	public Object copy(CopyBuilder copyBuilder) {
		return copyTo(new Point(), copyBuilder);
	}
	
	public void accept(GeometryVisitor visitor) {
		visitor.visit(this);
	}

	public <T> T accept(GeometryFunctor<T> visitor) {
		return visitor.apply(this);
	}
	
	public void accept(GMLVisitor visitor) {
		visitor.visit(this);
	}
	
	public <T> T accept(GMLFunctor<T> visitor) {
		return visitor.apply(this);
	}
	
}
